#include "dht11.h"

#define DHT11_GPIO_PORT GPIOB
#define DHT11_GPIO_PIN GPIO_PIN_5
#define DHT11_DQ_IN HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)
extern uint32_t fac_us;
void delay_us(uint32_t nus) // 100  6800
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD; // LOAD的值
    ticks = nus * fac_us;            // 计算延时需要计多少个数
    told = SysTick->VAL;             // 24  刚进入时的计数器值
    while (1)
    {
        tnow = SysTick->VAL; // 22  20  0
        if (tnow != told)
        {
            if (tnow < told)
                tcnt += told - tnow; // 这里注意一下SYSTICK是一个递减的计数器就可以了.
            else
                tcnt += reload - tnow + told;
            told = tnow;
            if (tcnt >= ticks)
                break; // 时间超过/等于要延迟的时间,则退出.
        }
    };
}
static void DHT11_IO_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

static void DHT11_IO_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // PUSHPULL
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

// 复位DHT11  \\起始
void DHT11_Rst(void)
{
    DHT11_IO_OUT();                                        // SET OUTPUT  转换成输出模式
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); // 拉低DQ
    HAL_Delay(20);                                         // 拉低至少18ms
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET); // DQ=1
    delay_us(30);
	                                          // 主机拉高20~40us
}

// 等待DHT11的回应
// 返回1:未检测到DHT11的存在/响应出问题
// 返回0:存在 响应合格
uint8_t DHT11_Check(void)
{
    uint8_t retry = 0;
    DHT11_IO_IN();                      // SET INPUT 转换成输入模式
    while (!DHT11_DQ_IN && retry < 100) // DHT11会拉低40~80us
    {
        // 接收到高电平退出while
        // while进入的时间大于100us,当我等了100us还不见你拉高，那也退出
        retry++;
        delay_us(1);
    }
    if (retry >= 100)
        return 1;
    else
        retry = 0;
    while (DHT11_DQ_IN && retry < 100) // DHT11拉低后会再次拉高40~80us
    {
        // 检测到低电平   或者时间大于100us退出
        retry++;
        delay_us(1);
    }
    if (retry >= 100)
        return 1;
    return 0;
}

// 从DHT11读取一个位
// 返回值：1/0
uint8_t DHT11_Read_Bit(void)
{
    uint8_t retry = 0;
    while (DHT11_DQ_IN) // 等待变为低电平
    {
        // 检测到低电平 或者 时间大于100us
        retry++;
        delay_us(1);
    }
    retry = 0;
    while (!DHT11_DQ_IN) // 等待变高电平
    {
        // 检测到高电平 或者 时间大于100us
        retry++;
        delay_us(1);
    }
    delay_us(40); // 等待40us
    if (DHT11_DQ_IN)
        return 1;
    else
        return 0;
}

// 从DHT11读取一个字节
// 返回值：读到的数据
uint8_t DHT11_Read_Byte(void)
{
    uint8_t i, dat;
    dat = 0;
    for (i = 0; i < 8; i++)
    {
        dat <<= 1;
        dat |= DHT11_Read_Bit();
    }
    return dat;
}

// 从DHT11读取一次数据
// temp:温度值(范围:0~50°)
// humi:湿度值(范围:20%~90%)
// 返回值：HAL_OK,正常;1,读取失败
uint8_t DHT11_Read_Data(uint8_t *humiH, uint8_t *humiL, uint8_t *tempH, uint8_t *tempL)
{
    uint8_t buf[5];
    uint8_t i;
    DHT11_Rst(); // 发送起始信号
    if (DHT11_Check() == 0)
    {
        for (i = 0; i < 5; i++) // 读取40位数据
        {
            buf[i] = DHT11_Read_Byte();
        }
        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
        {
            *humiH = buf[0];
            *humiL = buf[1];
            *tempH = buf[2];
            *tempL = buf[3];
        }
    }
    else
        return HAL_ERROR;

    return HAL_OK;
}

// 初始化DHT11的IO口 DQ 同时检测DHT11的存在
// 返回1:不存在
// 返回0:存在
uint8_t FS_DHT11_Init(void)
{
    // 初始化PB7引脚为推挽输出
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   // 推挽输出 push pull
    GPIO_InitStruct.Pull = GPIO_PULLUP;           // 上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);

    // GPIOB  PIN7
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET); // 输出高
    DHT11_Rst();                                           // 复位DHT11 //发送起始信号
    return DHT11_Check();                                  // 等待DHT11的回应  //检测DHT11响应
}
